package com.foreveross.proxyip.application.impl.offer;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

import javax.ejb.Remote;
import javax.ejb.Stateless;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.dayatang.domain.InstanceFactory;
import com.foreveross.proxyip.application.offer.IProxyIPService;
import com.foreveross.proxyip.application.offer.bean.ProxyIpDTO;
import com.foreveross.proxyip.core.BaseCode;
import com.foreveross.proxyip.core.pools.AvailablePool;
import com.foreveross.proxyip.core.pools.ChannelPool;
import com.foreveross.proxyip.core.pools.PoolsManager;
import com.foreveross.proxyip.core.redis.RedisExecutor;
import com.foreveross.proxyip.entity.ApplyIpLogEntity;
import com.foreveross.proxyip.entity.ChannelEntity;
import com.foreveross.proxyip.entity.ProxyIpBean;
import com.foreveross.proxyip.infra.cache.BufferCacheManager;
import com.foreveross.proxyip.infra.cache.BufferInstance;
import com.foreveross.proxyip.infra.cache.Operations;
import com.foreveross.proxyip.infra.conf.PropertyUtils;
import com.foreveross.proxyip.infra.conf.system.DefaultPropertiesLoader;

/**
 * 代理IP服务提供的EJB实现
 * 
 * @author xiangsf 2013-05-30
 * 
 */
/* EJB支持，请application对象申明成一个EJB对象 */
@Stateless(name = "IProxyIPService")
@Remote(IProxyIPService.class)
@Service("proxyIPEjbService")
@SuppressWarnings("unchecked")
public class ProxyIPEjbServiceImpl implements IProxyIPService {

	private static final String AUTH_ERROR = "口令错误！";
	protected static Logger logger = LoggerFactory.getLogger(ProxyIPEjbServiceImpl.class);
	//public static final CountDownLatch latch = new CountDownLatch(1);

	Integer cacheEntitycount = null;

	private PoolsManager poolsManager;
	
	// //
	// private IBaseRepository baseRepository;
	public PoolsManager getPoolsManager() {
		if (poolsManager == null) {
			poolsManager = InstanceFactory.getInstance(PoolsManager.class);
		}
		try {
			// markfb
			// latch.await();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return poolsManager;
	}

	@Override
	public ProxyIpDTO getProxyIp(String auth, String clientIp) throws Exception {
		Long applyTime = System.currentTimeMillis();
		ApplyIpLogEntity log = new ApplyIpLogEntity();
		log.setClientIp(clientIp);
		ChannelPool pool = getPoolsManager().getDefaultChannelPool();

		if (StringUtils.isNotBlank(auth) && auth.equals(pool.auth)) {
			ProxyIpBean proxyIp = pool.offerIp(null);
			if (proxyIp != null) {
				String message = "offer ip =" + proxyIp.getCompletionIP();
				log.setIps(proxyIp.getCompletionIP());
				log.setMessage(message);
				// logger.info(message);
				this.saveLog(log, pool.channel, applyTime, 1, null);
				return new ProxyIpDTO(proxyIp.getIp(), proxyIp.getPort());
			} else {// 如果默认池没有ip，从公共池拿ip
				proxyIp = getPoolsManager().getPublicPool().offerIp(null);
				if (proxyIp != null) {
					log.setMessage("默认池从公共池拿ip");
					this.saveLog(log, pool.channel, applyTime, 1, null);
					return new ProxyIpDTO(proxyIp.getIp(), proxyIp.getPort());
				} else {
					logger.warn("客户端 {} 获取ip为空!", clientIp);
					log.setMessage("offer ip is null");
					this.saveLog(log, pool.channel, applyTime, 1, null);
				}
			}
		} else {
			log.setMessage(AUTH_ERROR);
			this.saveLog(log, pool.channel, applyTime, 0, null);
			throw new RuntimeException(AUTH_ERROR);
		}
		
		logger.warn("客户端 {} 获取ip为空!", clientIp);
		
		return null;
	}

	@Override
	public List<ProxyIpDTO> getProxyIps(int size, String auth, String clientIp)
			throws Exception {
		Long applyTime = System.currentTimeMillis();
		ApplyIpLogEntity log = new ApplyIpLogEntity();
		log.setClientIp(clientIp);
		List<ProxyIpDTO> ips = new ArrayList<ProxyIpDTO>();
		ChannelPool pool = getPoolsManager().getDefaultChannelPool();
		
		if (StringUtils.isNotBlank(auth) && auth.equals(pool.auth)) {
			List<ProxyIpBean> ipList = pool.offerIps((long) size, null);
			StringBuffer ipStr = new StringBuffer();
			
			for (ProxyIpBean ip : ipList) {
				ips.add(new ProxyIpDTO(ip.getIp(), ip.getPort()));
				ipStr.append(ip.getCompletionIP()).append(",");
			}
			
			if (StringUtils.isNotBlank(ipStr)) {
				StringBuffer buf = new StringBuffer();
				
				if (size > ipList.size()) {
					logger.warn("客户端 {} 获取ip服务器提供不足!", clientIp);
					buf.append("渠道池ip提供不足； \n");
				}
				
				buf.append("offer ip = " + StringUtils.substringBeforeLast(ipStr.toString(), ","));
				log.setMessage(buf.toString());
				log.setIps(StringUtils.substringBeforeLast(ipStr.toString(), ","));
			} else {
				logger.warn("客户端 {} 获取ip为空!", clientIp);
				log.setMessage("无ip提供。");
			}
			this.saveLog(log, pool.channel, applyTime, 1, null);
			return ips;
		} else {
			log.setMessage(AUTH_ERROR);
			this.saveLog(log, pool.channel, applyTime, 0, null);
			throw new RuntimeException(AUTH_ERROR);
		}
	}

	@Override
	public ProxyIpDTO getChannelProxyIp(String channel, String auth,
			String clientIp) throws Exception {
		boolean isOnlyDefalutChannel = PropertyUtils.getBooleanProperty("proxyip.isOnlyDefalutChannel", DefaultPropertiesLoader.CONFIG_FILE);
		Long applyTime = System.currentTimeMillis();
		ApplyIpLogEntity log = new ApplyIpLogEntity();
		log.setClientIp(clientIp);
		ChannelPool pool = getPoolsManager().getChannelPoolMap().get(channel);
		ProxyIpBean proxyIp = null;
		// 保证唯一，当每获取一个后都要删除
		AvailablePool currPool = null;
		
		if (pool == null) {
			// 如果只开默认池，则不用重新注册
			if (!isOnlyDefalutChannel) {
				getPoolsManager().register(channel);
			}

			log.setMessage(String.format("%s 还未创建或不存在从默认池池拿ip", channel));
			currPool = getPoolsManager().getDefaultChannelPool();
			proxyIp = currPool.offerIp(channel);
			
			if (proxyIp == null) {
				currPool = getPoolsManager().getPublicPool();
				proxyIp = getPubicNoUsedIp();
			}
			
			log.setMessage(String.format("%s 还未创建或不存在从公共池拿ip", channel));
			
			logger.info("cleint:{}, channel:{}, 给出ip:{}", clientIp, channel, proxyIp.getCompletionIP());
			
			return new ProxyIpDTO(proxyIp.getIp(), proxyIp.getPort());
		}
		
		if (StringUtils.isNotBlank(auth) && auth.equals(pool.auth)) {
			proxyIp = pool.offerIp(null);
			
			if (proxyIp != null) {
				String message = "offer ip =" + proxyIp.getCompletionIP();
				log.setIps(proxyIp.getCompletionIP());
				log.setMessage(message);
				this.saveLog(log, pool.channel, applyTime, 1, null);
				ProxyIpDTO dto = new ProxyIpDTO(proxyIp.getIp(), proxyIp.getPort());
				dto.setChannelId(channel);
				pool.addIpOutChannelTime(proxyIp);
				
				logger.info("cleint:{}, channel:{}, 给出ip:{}", clientIp, channel, proxyIp.getCompletionIP());
				
				return dto;
			} else {// 如果渠道池没有ip从默认池拿ip
				log.setMessage(pool.getPoolName() + "从默认池拿ip");
				currPool = getPoolsManager().getDefaultChannelPool();
				proxyIp = currPool.offerIp(channel);
				
				
				if (proxyIp == null) {// 如果默认吃没有ip，从公共池拿ip
					log.setMessage(pool.getPoolName() + "从公共池拿ip");
					currPool = getPoolsManager().getPublicPool();
					proxyIp = getPubicNoUsedIp();
				}
				
				if (proxyIp != null) {
					log.setIps(proxyIp.getCompletionIP());
					this.saveLog(log, pool.channel, applyTime, 1, null);
					pool.addIpOutChannelTime(proxyIp);
					
					logger.info("cleint:{}, channel:{}, 给出ip:{}", clientIp, channel, proxyIp.getCompletionIP());
					
					return new ProxyIpDTO(proxyIp.getIp(), proxyIp.getPort());
				} else {
					logger.warn("客户端 {} 获取ip为空!", clientIp);
					log.setMessage("offer ip is null");
					this.saveLog(log, pool.channel, applyTime, 1, null);
				}
			}
			
		} else {
			log.setMessage(AUTH_ERROR);
			this.saveLog(log, pool.channel, applyTime, 0, null);
			throw new RuntimeException(AUTH_ERROR);
		}
		
		logger.warn("客户端 {} 获取ip为空!", clientIp);
		
		return null;
	}

	@Override
	public List<ProxyIpDTO> getChannelProxyIps(String channel, int size,
			String auth, String clientIp) throws Exception {
		ChannelPool pool = getPoolsManager().getChannelPoolMap().get(channel);
		boolean isOnlyDefalutChannel = PropertyUtils.getBooleanProperty("proxyip.isOnlyDefalutChannel", DefaultPropertiesLoader.CONFIG_FILE);

		// 如果池还未创建，则从注册创建池且此次从公共池获取
		if (pool == null) {
			// 如果只开默认池，则不用重新注册
			if (!isOnlyDefalutChannel) {
				getPoolsManager().register(channel);
			}

			pool = getPoolsManager().getDefaultChannelPool();
		}
		
		Long applyTime = System.currentTimeMillis();
		ApplyIpLogEntity log = new ApplyIpLogEntity();
		log.setClientIp(clientIp);
		List<ProxyIpDTO> ips = new ArrayList<ProxyIpDTO>();
		
		if (StringUtils.isNotBlank(auth) && auth.equals(pool.auth)) {
			List<ProxyIpBean> ipList = pool.offerIps((long) size, channel);
			StringBuffer ipStr = new StringBuffer();
			
			for (ProxyIpBean ip : ipList) {
				ProxyIpDTO dto = new ProxyIpDTO(ip.getIp(), ip.getPort());
				dto.setChannelId(channel);
				ips.add(dto);
				ipStr.append(ip.getCompletionIP()).append(",");
			}
			
			while (size - ips.size() > 0 && System.currentTimeMillis() - applyTime < 60000) {
				ips.add(getChannelProxyIp(channel, auth, clientIp));
			}
			
			for (ProxyIpDTO ip : ips) {
				logger.info("cleint:{}, channel:{}, 给出ip:{}", clientIp, channel, ip.getCompletionIP());
			}
			
			if (StringUtils.isNotBlank(ipStr)) {
				if (size > ipList.size()) {
					logger.warn("当道池ip提供不足!");
					log.setMessage("渠道池ip提供不足； \n");
				}
				
				log.setMessage("offer ip = " + StringUtils.substringBeforeLast(ipStr.toString(), ","));
				
				log.setIps(StringUtils.substringBeforeLast(ipStr.toString(), ","));
			} else {
				logger.warn("客户端 {} 获取ip为空!", clientIp);
				log.setMessage("无ip提供。");
			}
			
			this.saveLog(log, pool.channel, applyTime, 1, null);
			return ips;
		} else {
			log.setMessage(AUTH_ERROR);
			this.saveLog(log, pool.channel, applyTime, 0, null);
			throw new RuntimeException(AUTH_ERROR);
		}
	}

	@Override
	public boolean recycle(final ProxyIpDTO proxyIp, String auth) throws Exception {
		boolean isOnlyDefalutChannel = PropertyUtils.getBooleanProperty("proxyip.isOnlyDefalutChannel", DefaultPropertiesLoader.CONFIG_FILE);
		ChannelPool pool = null;
				
		if (proxyIp == null) {
			throw new RuntimeException("回收对象不能为空！");
		} else if (proxyIp.getChannelId() == null) {
			throw new RuntimeException("回收对象渠道‘channelId’==null。");
		}

		if (isOnlyDefalutChannel) {
			pool = this.getPoolsManager().getDefaultChannelPool();
		} else {
			pool = this.getPoolsManager().getChannelPoolMap().get(proxyIp.getChannelId());
		}
		
		final ChannelPool rDpool = pool;
		
		if (StringUtils.isNotBlank(auth) && auth.equals(pool.auth)) {
			pool.removeMarkIpAlreadyUsed(proxyIp.getCompletionIP());
			
			// 如果proxyIp标识为某渠道不可用，则需要设置标识，或者从公共池中丢弃
			if (proxyIp != null && ProxyIpDTO.STATUS_UNABLE.equals(proxyIp.getState())) {
				logger.info("ip[{}]渠道[{}]己不可用，代理回收", proxyIp.getCompletionIP(), pool.getPoolName());
				
				if (pool.isOpenIpUniqueness && pool.contains(proxyIp.getCompletionIP())) {
					pool.remove(proxyIp.getCompletionIP());
					return true;
				}
				
				// 如果ip所有渠道都不可用, 则删除公有池与渠道
			} else if (ProxyIpDTO.STATUS_ALL_UNABLE.equals(proxyIp.getState())){
				logger.info("ip[{}]所有渠道己不可用，代理回收", proxyIp.getCompletionIP());
				
				if (pool.isOpenIpUniqueness && pool.contains(proxyIp.getCompletionIP())) {
					pool.remove(proxyIp.getCompletionIP());
				}
				
				new RedisExecutor<Boolean>() {
					@Override
					public Boolean run() throws Exception {
						tran = jedis.multi();
						rDpool.ipStoreMap.remove(tran, proxyIp.getCompletionIP());
						tran.exec();
						return null;
					}
				}.exec();
		
				return true;
			}
		} else {
			throw new RuntimeException(AUTH_ERROR);
		}
		
		return false;
	}

	public void saveLog(ApplyIpLogEntity log, ChannelEntity channel,
			Long applyTime, int state, String clientIp) {
		if (log == null) {
			log = new ApplyIpLogEntity();
		}
		Long currTime = System.currentTimeMillis();
		log.setReponseTime(currTime - applyTime);
		log.setChannelCode(channel.getCode());
		log.setChannelName(channel.getName());
		log.setCreateTime(new Date());
		log.setClientIp(clientIp);
		log.setState(state);
		
		BufferInstance bufferinstance = new BufferInstance(log, Operations.CREATE);
		
		if (cacheEntitycount == null) {
			try {
				cacheEntitycount = Integer.parseInt(BaseCode.getValue("TASKQUEUELOG_CACHEENTITYCOUNT"));
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}
		
		BufferCacheManager.getInstance(cacheEntitycount).excure(bufferinstance, "applyIpLog");
		// baseRepository.transSave(log);
		// log.setIps(ips);
		// log.setMessage(message);
		// log.setReponseTime(reponseTime);
		// log.setReponseTime(state);
	}

	// @Override
	// public ProxyIpDTO getProxyIp(String auth) throws Exception {
	// return this.getProxyIp(auth, null);
	// }
	//
	// @Override
	// public List<ProxyIpDTO> getProxyIps(int size, String auth) throws
	// Exception {
	// return this.getProxyIps(size, auth, null);
	// }
	//
	// @Override
	// public ProxyIpDTO getChannelProxyIp(String channel, String auth)
	// throws Exception {
	// return this.getChannelProxyIp(channel, auth,null);
	// }
	//
	// @Override
	// public List<ProxyIpDTO> getChannelProxyIps(String channel, int size,
	// String auth) throws Exception {
	// return this.getChannelProxyIps(channel, size, auth, null);
	// }
	
	/**
	 * 获取公共池中没有被指定的ip
	 * 
	 * @return
	 * @throws Exception
	 */
	private ProxyIpBean getPubicNoUsedIp() throws Exception {
		AvailablePool publicPool = getPoolsManager().getPublicPool();
		List<String> ips  = publicPool.poolIpMap.getKeys(0L, -1L);
		ProxyIpBean bean  = null;
		
		if (ips == null) {
			return null;
		}
		
		for (String ip : new HashSet<String>(ips)) {
			
			if (publicPool.isOpenIpUniqueness && publicPool.isAlreadyDistributionChannel(ip)) {
				continue;
			}
			
			bean = (ProxyIpBean) publicPool.ipStoreMap.get(ip);
			
			if (bean != null) {
				return bean;
			}
		}
		
		return null;
	}
}
